﻿// ---------------------------------------------------------------------------------------------------------------------
//   Copyright (c) SRT Solutions 2009. All rights reserved.
// ---------------------------------------------------------------------------------------------------------------------

/* •—————————————————————————————————————————————————————————•
   | Adapted from MatthewPodwysocki's Functional C# Samples: |
   |                                                         |
   | http://code.msdn.microsoft.com/FunctionalCSharp         |
   •—————————————————————————————————————————————————————————• */

namespace SRTSolutions.Elevate
{
    using global::System;
    using global::System.Collections.Generic;

    /// <summary>
    /// An immutable 2-tuple class.
    /// </summary>
    /// <typeparam name="T1">The type of the first element.</typeparam>
    /// <typeparam name="T2">The type of the second element.</typeparam>
    /// <remarks>
    /// This class will be deprecated when VS2010 is released. At 
    /// that time, System.Tuple will be the preferred solution.
    /// </remarks>
#if NET_3_5
#else
    [Obsolete("This class was not part of the Base Class Library in .NET 3.5. In 4.0, use System.Tuple instead.")]
#endif
    public sealed class Tuple<T1, T2> : IEquatable<Tuple<T1, T2>>
    {
        private readonly T1 first;
        private readonly T2 second;

        /// <summary>
        /// Initializes a new instance of the <see cref="T:Tuple&lt;T1, T2&gt;"/> class.
        /// </summary>
        /// <param name="first">The first element.</param>
        /// <param name="second">The second element.</param>
        public Tuple(T1 first, T2 second)
        {
            this.first = first;
            this.second = second;
        }

        /// <summary>
        /// Gets the first element of the tuple.
        /// </summary>
        /// <value>The first element of the tuple.</value>
        public T1 First
        {
            get { return first; }
        }

        /// <summary>
        /// Gets the second element of the tuple.
        /// </summary>
        /// <value>The second element of the tuple.</value>
        public T2 Second
        {
            get { return second; }
        }
        
        /// <summary>
        /// Gets the first element of the tuple.
        /// </summary>
        /// <value>The first element of the tuple.</value>
        public T1 Item1
        {
            get { return first; }
        }

        /// <summary>
        /// Gets the second element of the tuple.
        /// </summary>
        /// <value>The second element of the tuple.</value>
        public T2 Item2
        {
            get { return second; }
        }


        /// <summary>
        /// Selects a value from this tuple using a function. Allows you to 
        /// assign descriptive names to the tuple elements to increase
        /// readability.
        /// </summary>
        /// <typeparam name="TResult">The type of the result.</typeparam>
        /// <param name="selector">The selector.</param>
        /// <returns></returns>
        public TResult Select<TResult>(Func<T1, T2, TResult> selector)
        {
            if (selector == null)
                throw new ArgumentNullException("selector", "selector is null.");

            return selector(First, Second);
        }

        /// <summary>
        /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
        /// <returns>
        /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
        /// </returns>
        /// <exception cref="T:System.NullReferenceException">
        /// The <paramref name="obj"/> parameter is null.
        /// </exception>
        public override bool Equals(object obj)
        {
            if (obj is Tuple<T1, T2>)
                return Equals((Tuple<T1, T2>)obj);

            return false;
        }

        /// <summary>
        /// Indicates whether the current object is equal to another object of the same type.
        /// </summary>
        /// <param name="other">An object to compare with this object.</param>
        /// <returns>
        /// true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
        /// </returns>
        public bool Equals(Tuple<T1, T2> other)
        {
            if (!EqualityComparer<T1>.Default.Equals(first, other.first))
                return false;
            if (!EqualityComparer<T2>.Default.Equals(second, other.second))
                return false;

            return true;
        }

        /// <summary>
        /// Serves as a hash function for a particular type.
        /// </summary>
        /// <returns>
        /// A hash code for the current <see cref="T:System.Object"/>.
        /// </returns>
        public override int GetHashCode()
        {
            int result = EqualityComparer<T1>.Default.GetHashCode(first);
            result ^= EqualityComparer<T2>.Default.GetHashCode(second);
            return result;
        }

        /// <summary>
        /// Implements the operator ==.
        /// </summary>
        /// <param name="left">The left value.</param>
        /// <param name="right">The right value.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator ==(Tuple<T1, T2> left, Tuple<T1, T2> right)
        {
            return left.Equals(right);
        }

        /// <summary>
        /// Implements the operator !=.
        /// </summary>
        /// <param name="left">The left value.</param>
        /// <param name="right">The right value.</param>
        /// <returns>The result of the operator.</returns>
        public static bool operator !=(Tuple<T1, T2> left, Tuple<T1, T2> right)
        {
            return !left.Equals(right);
        }
    }

    /// <summary>
    /// A factory class containing constructors for Tuples
    /// </summary>
    #pragma warning disable 0618
    public static class Tuple
    {
        /// <summary>
        /// Creates an immutable 2-Tuple.
        /// </summary>
        /// <typeparam name="T1">The type of the first element.</typeparam>
        /// <typeparam name="T2">The type of the second element.</typeparam>
        /// <param name="first">The first element.</param>
        /// <param name="second">The second element.</param>
        /// <returns>A new immutable 2-Tuple.</returns>
        public static Tuple<T1, T2> Create<T1, T2>(T1 first, T2 second)
        {
            return new Tuple<T1, T2>(first, second);
        }
    }
}
